package net.xdclass.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.CouponStateEnum;
import net.xdclass.enums.ProductOrderStateEnum;
import net.xdclass.enums.StockTaskStateEnum;
import net.xdclass.exception.BizException;
import net.xdclass.feign.ProductOrderFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.CouponRecordMapper;
import net.xdclass.mapper.CouponTaskMapper;
import net.xdclass.model.CouponRecordDO;
import net.xdclass.model.CouponRecordMessage;
import net.xdclass.model.CouponTaskDO;
import net.xdclass.model.LoginUser;
import net.xdclass.request.LockCouponRecordRequest;
import net.xdclass.service.CouponRecordService;
import net.xdclass.util.JsonData;
import net.xdclass.vo.CouponRecordVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author : zhen.xue
 * @date : 2021-05-26 17:50
 */
@Slf4j
@Service
public class CouponRecordServiceImpl implements CouponRecordService {

    @Autowired
    private CouponRecordMapper couponRecordMapper;

    @Autowired
    private CouponTaskMapper couponTaskMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private ProductOrderFeignService productOrderFeignService;

    @Override
    public Map<String, Object> page(int page, int size) {
        LoginUser user = LoginInterceptor.threadLocal.get();
        // 封装分页信息
        Page<CouponRecordDO> objectPage = new Page<>(page, size);
        IPage<CouponRecordDO> iPage = couponRecordMapper.selectPage(objectPage, new QueryWrapper<CouponRecordDO>()
                .eq("user_id", user.getId()).orderByDesc("create_time"));
        Map<String, Object> pageMap = new HashMap<>(3);
        pageMap.put("total_record",iPage.getTotal());
        pageMap.put("total_page",iPage.getPages());
        pageMap.put("current_data",iPage.getRecords().stream().map(this::beanProcess).collect(Collectors.toList()));
        return pageMap;
    }

    @Override
    public CouponRecordVO findById(long recordId) {
        LoginUser user = LoginInterceptor.threadLocal.get();
        CouponRecordDO couponRecordDO = couponRecordMapper.selectOne(new QueryWrapper<CouponRecordDO>().eq("id", recordId).eq("user_id", user.getId()));
        if(couponRecordDO == null) return null;
        return this.beanProcess(couponRecordDO);
    }

    @Override
    public JsonData lockCouponRecords(LockCouponRecordRequest lockCouponRecord) {
        // 获取用户信息
        LoginUser user = LoginInterceptor.threadLocal.get();
        String orderOutTradeNo = lockCouponRecord.getOrderOutTradeNo();
        List<Long> lockCouponRecordIds = lockCouponRecord.getLockCouponRecordIds();
        // 1 锁定优惠券记录
        int updateRows = couponRecordMapper.lockUseStateBatch(user.getId(), CouponStateEnum.USED.name(),lockCouponRecordIds);
        log.info("优惠券记录锁定updateRows={}",updateRows);
        // 2 task 插入记录
        List<CouponTaskDO> couponTaskDOList = lockCouponRecordIds.stream().map(obj -> {
            CouponTaskDO couponTaskDO = new CouponTaskDO();
            couponTaskDO.setCouponRecordId(obj);
            couponTaskDO.setCreateTime(new Date());
            couponTaskDO.setOutTradeNo(orderOutTradeNo);
            couponTaskDO.setLockState(StockTaskStateEnum.LOCK.name());
            return couponTaskDO;
        }).collect(Collectors.toList());
        int insertRows = couponTaskMapper.insertBatch(couponTaskDOList);
        log.info("新增优惠券记录task,insertRows={}",insertRows);
        if(lockCouponRecordIds.size() == insertRows && updateRows == insertRows){
            // 3 发送延迟消息，用来释放优惠券
            for (CouponTaskDO couponTaskDO : couponTaskDOList) {
                CouponRecordMessage couponRecordMessage = new CouponRecordMessage();
                couponRecordMessage.setMessageId(0L);
                couponRecordMessage.setOutTradeNo(couponTaskDO.getOutTradeNo());
                couponRecordMessage.setTaskID(couponTaskDO.getId());
                rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getCouponReleaseDelayRoutingKey(),couponRecordMessage);
                log.info("优惠券锁定消息发送成功：{}",couponRecordMessage);
            }
            return JsonData.buildSuccess();
        }else{
            throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
        }

    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    @Override
    public boolean releaseCouponRecord(CouponRecordMessage recordMessage) {
        // 解锁优惠券记录
        // 1 查询task工作单是否存在
        CouponTaskDO couponTaskDO = couponTaskMapper.selectOne(new QueryWrapper<CouponTaskDO>().eq("id", recordMessage.getTaskID()));
        if(couponTaskDO == null){
            log.warn("工作单不存在,消息:{}",recordMessage);
            return true;
        }
        // lock状态才处理
        if(StockTaskStateEnum.LOCK.name().equalsIgnoreCase(couponTaskDO.getLockState())){
            // 查询订单状态
            JsonData jsonData = productOrderFeignService.queryProductOrderState(recordMessage.getOutTradeNo());
            if(0 == jsonData.getCode()){
                // 正常响应判断订单状态
                String state = jsonData.getData().toString();
                // 状态是 new 状态
                if(ProductOrderStateEnum.NEW.name().equalsIgnoreCase(state)){
                    log.warn("订单状态是NEW,返回给消消息队列重新投递：{}",recordMessage);
                    return false;
                }
                // 已经支付
                if(ProductOrderStateEnum.PAY.name().equalsIgnoreCase(state)){
                    // 如果是已经支付,修改task状态为finish
                    couponTaskDO.setLockState(StockTaskStateEnum.FINISH.name());
                    couponTaskMapper.update(couponTaskDO,new QueryWrapper<CouponTaskDO>().eq("id",recordMessage.getTaskID()));
                    log.info("订单已支付,修改库存锁定工作单为FINIS状态");
                    return true;
                }
            }
            // 订单不存在或者订单被取消，确认消息即可修改task状态为CANCLE，回复优惠券使用记录
            log.warn("订单不存在或者订单被取消，确认消息即可修改task状态为CANCLE，回复优惠券使用记录,message:{}",recordMessage);
            couponTaskDO.setLockState(StockTaskStateEnum.CANCEL.name());
            couponTaskMapper.update(couponTaskDO,new QueryWrapper<CouponTaskDO>().eq("id",recordMessage.getTaskID()));
            // 恢复优惠券记录是new 状态
            couponRecordMapper.updateState(couponTaskDO.getCouponRecordId(),CouponStateEnum.NEW.name());
            return true;
        }
        log.warn("工作单状态不是LOCK,state={},消息体={}",couponTaskDO.getLockState(),recordMessage);
        return true;
    }

    private CouponRecordVO beanProcess(CouponRecordDO obj) {
        CouponRecordVO couponRecordVO = new CouponRecordVO();
        BeanUtils.copyProperties(obj,couponRecordVO);
        return couponRecordVO;
    }
}